/* 
 * File:   map.h
 * Author: bjorn
 *
 * Created on September 2, 2007, 11:55 AM
 */

#ifndef _TILEMAP_H
#define	_TILEMAP_H

#include <vector>
#include <list>
#include <SDL/SDL.h>

#include "image.h"
#include "object.h"
#include "screen.h"
#include "image.h"
#include "script.h"

    
#ifndef ERUTA_MAP_LAYER_MAX
    #define ERUTA_MAP_LAYER_MAX 3    
#endif    

#define MAPENTITY_NONE 		0 
#define MAPENTITY_PLAYER 	1
#define MAPENTITY_BEING 	2
#define MAPENTITY_OBJECT 	4
#define MAPENTITY_PROP 		8 

#define ENTITY_FEET_HIGH  40
#define ENTITY_FEET_WIDE  40

#define ENTITY_NORMAL_HIGH  80
#define ENTITY_NORMAL_WIDE  40


typedef int Entitykind;
 
struct Entitypoint {
	int x;
	int y;
	Entitypoint(int ix, int iy) {
		x = ix; y = iy;
	}
	Entitypoint() { Entitypoint(-1, -1); }
};

struct Entityspeed {
	int vx;
	int vy;
	Entityspeed() {	vx = vy = 0; }	 
};


struct Entitysize {
	int wide;
	int high;
};

struct Entityfeet {
	Entitypoint middle;
	Entitypoint topleft;
	Entitysize 	size;
	Entityspeed speed;
	int 		high;
	Entityfeet() {
		Entityfeet(-1, -1);
	}	 
	Entityfeet(int x, int y) {
		size.wide = 40;
		size.high = 40;
		topleft.x = x;
		topleft.y = y;
		middle.x  = topleft.x + (size.wide / 2);
		middle.y  = topleft.y + (size.high / 2);
		high = 0;
	}	
	
	void move_by(int vx, int vy) {
		topleft.x += vx; topleft.y += vy;
		middle.x  += vx; middle.y += vy;
	}
};
     
struct Mapentity {
	Rope name;
	Entitypoint topleft;
	Entitysize  size;
	Entityfeet  feet;   	// The 'feet is the area around the feet/paws, bottom part of the entity.
	Entitykind  kind;
	Entityspeed speed;
	int			maplevel; 	// The map level this entity is on.
	Image 		image; 		// Must become a sprite.
	Mapentity () {
		Mapentity(MAPENTITY_NONE, -1, -1, 0, 0);
	}
	
	Mapentity(Entitykind ki, int x, int y, int wide = ENTITY_NORMAL_WIDE, int high = ENTITY_NORMAL_HIGH) {
		kind = ki;
		topleft.x = x;
		topleft.y = y;
		size.wide = wide;
		size.high = high;
		feet 	  = Entityfeet(x, y + (high /2));
	}
	
	void draw (Screen screen, int screencorner_x, int screencorner_y) {
		if(kind < MAPENTITY_PLAYER) return;
		if(!image)   				return;
		int screen_x 				= topleft.x - screencorner_x;  
		int screen_y 				= topleft.y - screencorner_y;  
		printf("Draw to %d %d %s\n", topleft.x, topleft.y, name.c_str());
		screen.blit(screen_x, screen_y, image);
	}
	
	void update() {
		if(kind < MAPENTITY_PLAYER)	return;
		if(MAPENTITY_PROP == kind) 	return; 
		topleft.x += speed.vx;
		topleft.y += speed.vy;
		feet.move_by(speed.vx, speed.vy);
		printf("Moved to %d %d %s\n", topleft.x, topleft.y , name.c_str());
	}
	
};
  
typedef Mapentity * Mapentitypointer; 
typedef std::vector < Mapentitypointer > Mapentitylist;


#define TILE_NORMAL   (0)
/* A normal tile. */
#define TILE_BLOCK    (1)
/* A blocking tile */
#define TILE_SLIPPERY (2)
/* A slippery tile */
#define TILE_WATER    (4)
/* A tile under water */
#define TILE_PAIN     (8)
/* A tile that causes damage. */
#define TILE_PUSH_N   (16)
#define TILE_PUSH_E   (32)
#define TILE_PUSH_S   (64)
#define TILE_PUSH_W   (128)

typedef unsigned char Terrainkind;
typedef signed char   Terrainhigh;

#define TERRAIN_MOST_DEEP -120 
#define TERRAIN_MOST_HIGH  120 
#define TERRAIN_GROUND_HIGH  0 

#define MAPLAYER_TILE_HIGH 40
#define MAPLAYER_TILE_WIDE 40

#define MAPLAYER_TILES_MAX 10000
#define MAPLAYER_WIDE_MAX 1000
#define MAPLAYER_HIGH_MAX 1000


struct Terraintile {
  signed char _high;
  Terrainkind _kind;
};

struct Terrainlayer {
    Terraintile		   _layer[MAPLAYER_HIGH_MAX][MAPLAYER_WIDE_MAX];
    Terrainhigh        _water_high;
    Terrainhigh        _ground_high;
	
	Terrainlayer(Terrainhigh ground_high = TERRAIN_GROUND_HIGH, Terrainhigh water_high = TERRAIN_MOST_DEEP) {
		_water_high 	= water_high;
		_ground_high 	= ground_high;
	} 
	Terraintile  get(int x, int y) {
		return _layer[x][y];
	}
};

#define TILE_MAX_IMAGES 8

struct Maptile {
	Image images[TILE_MAX_IMAGES];
	int now;
	int max;
	Maptile() { now = 0; max = 0; } 

	int image_add(Image image) {
		if(TILE_MAX_IMAGES <= (max +1)) { return -1; }
		images[max] = image;
		max++;
	}

	Image image_get(int phase) {
		if(!max) return NULL;
		int now = phase % max;
		return images[now];
	}

};

typedef Maptile * Maptilepointer;

struct Maplayer {
    Maptilepointer _tiles[MAPLAYER_TILES_MAX];
    Maptilepointer _layer[MAPLAYER_HIGH_MAX][MAPLAYER_WIDE_MAX];
    Terrainlayer _terrain();
    long _wide;
    long _high;
    long _tilecount;
    int  _tilewide;
    int  _tilehigh;
	int  _phase;
	int  _delay;
	Maplayer() { _wide = 0 ; _high = 0; _phase = 0; _delay = SDL_GetTicks(); } ; 
	Maplayer(long wide, long high); 
	~Maplayer();
	int 		add(Maptilepointer tile); 
	Maplayer const *	set(int x, int y, int tile_id, int phase = 0); 
	Image 		get(int x, int y); 
	Maplayer const *	draw(Screen target, int x, int y ); 
};

struct Tilemap {
	int		 		layer_count;
	Maplayer 		layers[ERUTA_MAP_LAYER_MAX];
	int 	 		wide;
	int 	 		high;
	Image    		background;
	Mapentitylist	entities[ERUTA_MAP_LAYER_MAX];
	Entitypoint 	topleft;
	Script          *script;
/*	Tilemap(int w, int h) {
		wide = w;
		high = h;
		layer_count = 1;
		layers[1] = Maplayer(w, h);
	}	
*/
	void scroll_to(int x, int y) {
		topleft.x = x;
		topleft.y = y;
	}
	
	void scroll_by(int dx, int dy) {
		topleft.x += dx;
		topleft.y += dy;
	}
	
	Entitypoint map2screen(int map_x, int map_y) {
		Entitypoint result = Entitypoint(map_x - topleft.x, map_y - topleft.y);
		return result;  
	}

	void entities_update_layer(int layer) {
		Mapentitylist entaid = entities[layer];
		int stop = entaid.size();
		for(int index = 0; index < stop; index ++) {
			Mapentitypointer mapen = entaid[index];
			if(!mapen) continue;
			mapen->update();
		} 
	} 

	void entities_update () {
		int index = 0;
		for(int index = 0 ; index < layer_count ; index++){
			entities_update_layer(index);
		}
	}

		
	void drawlayer(Screen screen, int layer) {
		layers[layer].draw(screen, topleft.x, topleft.y );
		Mapentitylist entaid = entities[layer];
		int stop = entaid.size();
		for(int index = 0; index < stop; index ++) {
			Mapentitypointer mapen = entaid[index];
			if(!mapen) continue;
			mapen->draw(screen, topleft.x, topleft.y);
		} 
	} 

	void draw (Screen screen) {
		int index = 0;
		// XXX.. also draw background if present
		for(int index = 0 ; index < layer_count ; index++){
			drawlayer(screen, index);
		}
	}
	
	Rope lua_export(); 
	bool lua_import(Rope input);
};


#endif	/* _MAP_H */

